namespace Brjnk.GpxExplorer.Workspace.ApplicationStates
{
    using System;
    using System.Collections;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Linq;
    using Brjnk.Common;
    using Brjnk.GpxExplorer.ViewModel;

    /// <summary>
    /// </summary>
    public class MovePointApplicationState : ApplicationState, INotifyPropertyChanged
    {
        private MapManager map;
        private IMoveablePoint point;
        private ObservableCollection<GpsCoordinates> history = new ObservableCollection<GpsCoordinates>();

        public MovePointApplicationState(IMoveablePoint point, MapManager map)
        {
            this.map = map;
            this.point = point;

            map.MapClicked += new EventHandler<MapClickEventArgs>(map_MapClicked);
            OriginalPosition = point.PositionI;
            NewPosition = point.PositionI;
            Distance = 0;
            historyRO = new ReadOnlyObservableCollection<GpsCoordinates>(history);

            CreateCommands();
        }

        private void map_MapClicked(object sender, MapClickEventArgs e)
        {
            history.Add(point.PositionI);
            UndoCommand.IsAvailable = history.Count > 0;
            NewPosition = e.GpsPosition;
            point.Move(e.GpsPosition);
            CalculateDistance();
        }

        private void CalculateDistance()
        {
            Distance = OriginalPosition - NewPosition;
        }

        private GpsCoordinates originalPosition;

        public GpsCoordinates OriginalPosition
        {
            get { return originalPosition; }
            private set { originalPosition = value; OnPropertyChanged("OriginalPosition"); }
        }

        private GpsCoordinates newPosition;

        public GpsCoordinates NewPosition
        {
            get { return newPosition; }
            private set { newPosition = value; OnPropertyChanged("NewPosition"); }
        }

        private Distance distance = 0;

        public Distance Distance
        {
            get { return distance; }
            private set { distance = value; OnPropertyChanged("Distance"); }
        }

        public override void Abort()
        {
            map.MapClicked -= new EventHandler<MapClickEventArgs>(map_MapClicked);
        }

        public override IEnumerable AbortAfterObjectRemoved
        {
            get { return Enumerable.Repeat(point, 1); }
        }

        public override bool AvailableInGraphMode
        {
            get { return false; }
        }

        public override bool AvailableInMapMode
        {
            get { return true; }
        }

        public ReadOnlyObservableCollection<GpsCoordinates> historyRO;

        public ReadOnlyObservableCollection<GpsCoordinates> History { get { return historyRO; } }

        private void Undo()
        {
            var last = history.Last();
            history.RemoveAt(history.Count - 1);
            UndoCommand.IsAvailable = history.Count > 0;

            NewPosition = last;
            point.Move(last);
            CalculateDistance();
        }

        private void Complete()
        {
            map.MapClicked -= new EventHandler<MapClickEventArgs>(map_MapClicked);
            OnEnd();
        }

        private void Cancel()
        {
            map.MapClicked -= new EventHandler<MapClickEventArgs>(map_MapClicked);
            point.Move(OriginalPosition);
            OnEnd();
        }

        private void CreateCommands()
        {
            UndoCommand = new GenericCommand(o => Undo());
            UndoCommand.IsAvailable = history.Count > 0;
            CompletedCommand = new GenericCommand(o => Complete());
            CancelCommand = new GenericCommand(o => Cancel());
        }

        public GenericCommand UndoCommand { get; private set; }

        public GenericCommand CompletedCommand { get; private set; }

        public GenericCommand CancelCommand { get; private set; }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}